package com.supor.aiot.common.helper;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.text.TextUtils;

import com.android.baseconfig.common.utils.Logc;
import com.android.baseconfig.common.utils.ToastUtils;
import com.android.baseconfig.common.utils.Util;
import com.android.baseconfig.common.views.AppBasicDialog;
import com.supor.aiot.common.R;
import com.supor.aiot.common.analysis.AnalysisTool;
import com.supor.aiot.common.analysis.event.BindFailureEvent;
import com.supor.aiot.common.app.Constants;
import com.supor.aiot.common.entity.RecordBean;
import com.supor.nfc.lib.common.BuildHelper;
import com.supor.nfc.lib.utils.HexUtil;
import com.supor.nfc.lib.utils.NfcUtils;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

/**
 * ---------------------------------------------------------------- <br>
 * Copyright (C) 2014-2020, by het, Shenzhen, All rights reserved.  <br>
 * ---------------------------------------------------------------- <br>
 * <p>
 * 描述: NFCHelper <br>
 * 作者: lei <br>
 * 日期: 2020/12/4 <br>
 */
public class NFCHelper {
    private static final String TAG = "NFCHelper";
    private static NFCHelper instance = null;
    /**
     * NFC版本：0-初始版本，0x30block写入|1-迭代版本，增加record4快应用，0x60block写入
     */
    private int nfcVersion = 0;

    public static NFCHelper getInstance() {
        if (instance == null) {
            instance = new NFCHelper();
        }
        return instance;
    }

    public int getNfcVersion() {
        return nfcVersion;
    }

    /**
     * 处理NFC中的数据
     *
     * @param intent intent
     */
    public RecordBean processIntent(Intent intent) {
        String action = intent.getAction();
        Logc.i("action: " + action);

        // 读取NDEF data
        if (action == null || !(action.equalsIgnoreCase(NfcAdapter.ACTION_NDEF_DISCOVERED)
                || action.equalsIgnoreCase(NfcAdapter.ACTION_TECH_DISCOVERED)
                || action.equalsIgnoreCase(NfcAdapter.ACTION_TAG_DISCOVERED))) {
            Logc.e("not nfc ndef data");
            return null;
        }
        nfcVersion = 0;
        RecordBean bean = null;
        String cValue = "";
        NdefRecord[] ndefRecords = NfcUtils.readNdefRecordsFromTag(intent);
        if (ndefRecords != null) {
            for (NdefRecord ndefRecord : ndefRecords) {
                String nfcType = new String(ndefRecord.getType(), StandardCharsets.UTF_8);
                String payload = new String(ndefRecord.getPayload(), StandardCharsets.UTF_8);
                Logc.d("type: " + nfcType + ", payload: " + payload);
                if ("U".equalsIgnoreCase(nfcType)) {
                    if (null != ndefRecord.toUri()) {
                        Uri uri = ndefRecord.toUri();
                        cValue = uri.getQueryParameter("c");
//                bean.setR0(ndefRecord.toUri().toString());
                        Logc.i("R1: type: " + nfcType + ", payload: " + new String(ndefRecord.getPayload(), StandardCharsets.UTF_8) + " uriPath " + ndefRecord.toUri().toString());
                    }
                }
                if (payload.contains(Constants.NFC_SCHEMA)) {
                    bean = getRecordInfo(payload);
                } else if ("hap".equalsIgnoreCase(nfcType)) {
                    nfcVersion = 1;
                }
            }
        } else {
            Logc.e("ndefRecords is null");
        }
        //获取UID
        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        //获取卡id这里即uid，字节数组类型
        byte[] ids = tagFromIntent != null ? tagFromIntent.getId() : null;
        if (bean != null && ids != null) {
            bean.setUid(HexUtil.encodeHexStr(ids).toUpperCase());
            bean.setcValue(cValue);
        }
        return bean;
    }

    /**
     * 获取NFC中的pkg
     *
     * @param intent intent
     * @return pkg
     */
    public String checkPKG(Intent intent) {
        NdefRecord[] ndefRecords = NfcUtils.readNdefRecordsFromTag(intent);
        if (ndefRecords != null) {
            for (NdefRecord ndefRecord : ndefRecords) {
                String nfcType = new String(ndefRecord.getType(), StandardCharsets.UTF_8);
                String payload = new String(ndefRecord.getPayload(), StandardCharsets.UTF_8);
                if (nfcType.contains(Constants.NFC_PKG)) {
                    return payload;
                }
            }
        }
        return "";
    }

    private RecordBean getRecordInfo(String payload) {
        String params = payload.substring(payload.indexOf("?") + 1);
        Logc.d("record params: " + params);
        String[] paramArray = params.split("&");
        if (paramArray.length < 1) {
            return null;
        }
        RecordBean bean = new RecordBean();
        for (String s : paramArray) {
            String[] param = s.split("=");
            if (param.length == 2 && param[0] != null && !"".equals(param[0])) {
                if ("s".equalsIgnoreCase(param[0]) || "sn".equalsIgnoreCase(param[0])) {
                    bean.setSn(param[1]);
                } else if ("p".equalsIgnoreCase(param[0])) {
                    bean.setProductId(param[1]);
                } else if ("m".equalsIgnoreCase(param[0]) || "mac".equalsIgnoreCase(param[0])) {
                    bean.setWifiMac(param[1]);
                } else if ("c".equalsIgnoreCase(param[0]) || "conn".equalsIgnoreCase(param[0])) {
                    bean.setIsWifiConnect(param[1]);
                } else if ("cnt".equalsIgnoreCase(param[0]) || "count".equalsIgnoreCase(param[0])) {
                    bean.setCount(param[1]);
                } else if ("e".equalsIgnoreCase(param[0])) {
                    bean.setError(param[1]);
                }
            }
        }
        return bean;
    }

    public boolean processRecord(Context context, Intent intent, RecordBean recordBean,
                                 String gizIp, String ssid, String pass, String bssid) {
        if (recordBean != null) {
            if (TextUtils.isEmpty(recordBean.getWifiMac())) {
                ToastUtils.getInstance().showCustomToast("读取产品MAC地址失败。");
                analysisBindFailure(BindFailureEvent.Value.REASON_MAC_NOT_FOUND, ssid);
                showNFCFailureDialog(context);
                return false;
            }
            if (recordBean.getProductId() == null) {
                // ToastUtils.getInstance().showCustomToast("未获取到产品ID");
                Logc.e("onNewIntent: can't read productId");
                // recordBean.setProductId(Constants.Type.CHEF_MACHINE);
                // return;
            }
            // todo modify by lei  2021/09/25
            // if (!recordBean.getProductId().equalsIgnoreCase(productModel.pid)) {
            //     ToastUtils.getInstance().showCustomToast("机器型号不匹配");
            //     Logc.e("onNewIntent: product not match");
            //     return;
            // }

            byte[] data;
            String tempSsid = ssid;
            // ssid有中文时转为GBK编码字符串
            if (Util.hasChineseChar(tempSsid)) {
                try {
                    tempSsid = new String(tempSsid.getBytes("GBK"),
                            StandardCharsets.ISO_8859_1);
                } catch (UnsupportedEncodingException e) {
                    Logc.e("UnsupportedEncodingException: GBK");
                    e.printStackTrace();
                }
                data = BuildHelper.buildWiFiData(tempSsid, HexUtil.decodeHex(bssid), pass,
                        gizIp, true);
            } else {
                data = BuildHelper.buildWiFiData(tempSsid, HexUtil.decodeHex(bssid), pass,
                        gizIp, false);
            }

            boolean success = NFCHelper.getInstance().writeNfcA(intent, data);
            if (success) {
                return true;
            } else {
                Logc.e(TAG, "nfca write failed");
                analysisBindFailure(BindFailureEvent.Value.REASON_NFCA, ssid);
                showNFCFailureDialog(context);
            }
        } else {
            Logc.e(TAG, "record read failed");
            analysisBindFailure(BindFailureEvent.Value.REASON_RECORD, ssid);
            showNFCFailureDialog(context);
        }
        return false;
    }

    private void showNFCFailureDialog(Context context) {
        AppBasicDialog failureDialog;
        AppBasicDialog.Builder builder = new AppBasicDialog.Builder(context, false);
        builder.setTitle(R.string.nfc_touch_failure);
        builder.setMessage(R.string.nfc_touch_failure_tip);
        builder.setPositiveButton(R.string.sure, (dialog, which) -> dialog.cancel());
        failureDialog = builder.create();
        failureDialog.setCancelable(false);
        failureDialog.show();
    }

    private void analysisBindFailure(String reason, String ssid) {
        HashMap<String, String> params = new HashMap<>();
        params.put(BindFailureEvent.Param.BIND_TYPE, BindFailureEvent.Value.BIND_TYPE_NFC);
        params.put(BindFailureEvent.Param.USER_ID, GlobalDataCache.userInfo.getBossUserId());
        params.put(BindFailureEvent.Param.WIFI_NAME, ssid);
        params.put(BindFailureEvent.Param.BIND_STAGE, BindFailureEvent.Value.STAGE_TOUCH);
        params.put(BindFailureEvent.Param.FAILURE_REASON, reason);
        AnalysisTool.bindFailureEvent(params, true);
    }

    public boolean writeIsodep(Intent intent, byte[] data) {
        if (!NfcUtils.isodepConnect(intent)) {
            return false;
        }
        int dataLen = data.length;
        int len = data.length - 4;
        int count = 0;
        int block = 0x30;
        Logc.i("transceive data: " + dataLen);
        while (count < dataLen) {
            byte[] data4 = new byte[6];
            data4[0] = (byte) 0xA2;
            data4[1] = (byte) block;
            if (count <= len) {
                System.arraycopy(data, count, data4, 2, 4);
            } else {
                System.arraycopy(data, count, data4, 2, dataLen - count);
            }
            byte[] result = NfcUtils.isoTransceive(data4);
            if (result == null) {
                Logc.e("transceive failed: 0x%x" + block);
                return false;
            }
            Logc.i("transceive success: " + HexUtil.encodeHexStr(result));
            count += 4;
            block++;
        }
        return true;
    }

    /**
     * 读私有数据，byte[] data = {0x30, 0x60};  0x30表示读，0x60表示要读的block
     *
     * @param intent     intent
     * @param startBlock 开始block
     * @param endBlock   结束block
     * @return 读成功或失败
     */
    public byte[] readNfcA(Intent intent, int startBlock, int endBlock) {
        if (!NfcUtils.nfcAConnect(intent)) {
            return null;
        }
        if (startBlock > endBlock) {
            return null;
        }
        long time = System.currentTimeMillis();
        byte[] data = new byte[4 * (endBlock - startBlock + 1)];
        int i = 0;
        while (startBlock <= endBlock) {
            byte[] data2 = new byte[2];
            data2[0] = (byte) 0x30;
            data2[1] = (byte) startBlock;
            byte[] result = NfcUtils.nfcATransceive(data2);
            if (result == null) {
                Logc.e("nfca transceive failed: " + Integer.toHexString(startBlock));
                return null;
            }
            if (result.length == 16) {
                System.arraycopy(result, 0, data, 4 * i, Math.min(data.length - 4 * i, 16));
            } else {
                Logc.e("nfca transceive failed: not retrun 16 byte, " + Integer.toHexString(startBlock));
                return null;
            }
            startBlock += 4;
            i += 4;
        }
        return data;
    }

    /**
     * 写私有数据，通过NFCA写入，每次只能写四个字节，从30block开始，但是30block放在最后写入，
     * 目的是让配网标志位最后写入，充当校验
     *
     * @param intent intent
     * @param data 写入的数据
     * @return 写入成功或失败
     */
    public boolean writeNfcA(Intent intent, byte[] data) {
        if (!NfcUtils.nfcAConnect(intent)) {
            return false;
        }
        long time = System.currentTimeMillis();
        int dataLen = data.length;
        int startBlock = 0x30;
        if (nfcVersion == 1) {
            startBlock = 0x60;
        }
        int block = startBlock + 1;
        Logc.i("nfca transceive data: " + new String(data));
        int i = 0;
        int count = dataLen / 4;
        int remainder = dataLen % 4;
        while (i <= count) {
            byte[] data4 = new byte[6];
            data4[0] = (byte) 0xA2;
            data4[1] = (byte) block;
            if (i == count) {
                block = startBlock;
                data4[1] = (byte) startBlock;
                System.arraycopy(data, 0, data4, 2, 4);
            } else {
                if (i == count - 1) {
                    if (remainder != 0) {
                        System.arraycopy(data, 4 * (i + 1), data4, 2, remainder);
                    } else {
                        i++;
                        continue;
                    }
                } else {
                    System.arraycopy(data, 4 * (i + 1), data4, 2, 4);
                }
            }
            byte[] result = NfcUtils.nfcATransceive(data4);
            if (result == null) {
                Logc.e("nfca transceive failed: " + Integer.toHexString(block));
                return false;
            }
            if (result[0] != 0x0a) {
                Logc.e("nfca transceive failed, not 0a: " + Integer.toHexString(block));
                return false;
            }
            // String hexResult = HexUtil.encodeHexStr(result);
            // if (!"0a".equalsIgnoreCase(hexResult)) {
            //     Logc.e("nfca transceive failed, not 0a: " + Integer.toHexString(block));
            //     return false;
            // }
            // Logc.i("nfca transceive success: 0x" + hexResult + ", block: " + block);
            block++;
            i++;
        }
        Logc.d("nfca transceive over : " + (System.currentTimeMillis() - time));
        return true;
    }

    private void checkC(NdefRecord ndefRecord) {
        String nfcType = new String(ndefRecord.getType(), StandardCharsets.UTF_8);
        if ("U".equalsIgnoreCase(nfcType)) {
            if (null != ndefRecord.toUri()) {
                Uri uri = ndefRecord.toUri();
                String cValue = uri.getQueryParameter("c");
//                bean.setR0(ndefRecord.toUri().toString());
                Logc.e("R1: type: " + nfcType + ", payload: " + new String(ndefRecord.getPayload(), StandardCharsets.UTF_8) + " uriPath " + ndefRecord.toUri().toString());
            }
        }
    }
}
